/** @jsxImportSource @emotion/react */
import { jsx } from "@emotion/react";
import * as React from "react";
// import { IconButtonProps } from "./IconButton";
import { animated } from "@react-spring/web";
// import { useTheme } from "./Theme/Providers";
// import { mergeRefs } from "./Hooks/merge-refs";
import {
    useFloating,
    autoUpdate,
    offset,
    flip,
    shift,
    useClick,
    useDismiss,
    useRole,
    useInteractions,
    Placement,
    FloatingPortal,
    FloatingFocusManager,
    useId, useMergeRefs
} from "@floating-ui/react";
import { mergeRefs } from "react-merge-refs";
// import { useTheme } from "./Theme/Providers";
import {
    useTheme,
} from "customize-easy-ui-component";
import {ReactElement} from "react";


/*
根据例子修改的 https://codesandbox.io/s/distracted-swirles-jo1pvu?file=/src/App.tsx
* */

interface PopoverOptions {
    initialOpen?: boolean;
    placement?: Placement;
    modal?: boolean;
    open?: boolean;
    onOpenChange?: (open: boolean) => void;
}

export function usePopover({
                               initialOpen = false,
                               placement = "bottom",
                               modal,
                               open: controlledOpen,
                               onOpenChange: setControlledOpen
                           }: PopoverOptions = {}) {
    const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen);
    const [labelId, setLabelId] = React.useState<string | undefined>();
    const [descriptionId, setDescriptionId] = React.useState<
        string | undefined
    >();

    const open = controlledOpen ?? uncontrolledOpen;
    const setOpen = setControlledOpen ?? setUncontrolledOpen;

    const data = useFloating({
        placement,
        open,
        onOpenChange: setOpen,
        whileElementsMounted: autoUpdate,
        middleware: [
            offset(5),
            flip({
                crossAxis: placement.includes("-"),
                fallbackAxisSideDirection: "end",
                padding: 5
            }),
            shift({ padding: 5 })
        ]
    });

    const context = data.context;

    const click = useClick(context, {
        enabled: controlledOpen == null
    });
    const dismiss = useDismiss(context);
    const role = useRole(context);

    const interactions = useInteractions([click, dismiss, role]);

    return React.useMemo(
        () => ({
            open,
            setOpen,
            ...interactions,
            ...data,
            modal,
            labelId,
            descriptionId,
            setLabelId,
            setDescriptionId
        }),
        [open, setOpen, interactions, data, modal, labelId, descriptionId]
    );
}

type ContextType =
    | (ReturnType<typeof usePopover> & {
    setLabelId: React.Dispatch<React.SetStateAction<string | undefined>>;
    setDescriptionId: React.Dispatch<
        React.SetStateAction<string | undefined>
    >;
})
    | null;

const PopoverContext = React.createContext<ContextType>(null);
//原名是 usePopoverState
export const usePopoverContext = () => {
    const context = React.useContext(PopoverContext);

    if (context == null) {
        throw new Error("Popover components must be wrapped in <Popover />");
    }

    return context;
};

/*为适应横屏手机：限制内容高度，不应该超过4行的输入框高度。
类似Dialog 但一般页面占用尺寸较小的。默认点击外部关闭。默认跟随ref定位的。
旧的Sheet不能自适应也不配套TwoHalfFrame已经改用Popover替代;
遇到高度超出的，在html<body/>要加 overflow-y: auto; 若内容height太大自适应滚动就失去感觉了？还是BUG。
【特别注意】内容太大的，高度超出横屏手机屏幕height的情况，一定请改用Dialog; 高度超了Popover可能会出问题。
若没有加<PopoverRefer></PopoverRefer>导致缺省定位屏幕左上角,此时placement无效。高度大的不加<PopoverRefer滚动好控制。
* */
export function Popover({
                            children,
                            modal = false,
                            ...restOptions
                        }: {
    children: React.ReactNode;
} & PopoverOptions) {
    // This can accept any props as options, e.g. `placement`,
    // or other positioning options.
    const popover = usePopover({ modal, ...restOptions });
    return (
        <PopoverContext.Provider value={popover}>
            {children}
        </PopoverContext.Provider>
    );
}

interface PopoverReferProps {
    children: React.ReactNode;
    asChild?: boolean;
}
//保留 PopoverRefer 替代PopoverTrigger名称。 state改名context；
export const PopoverRefer = React.forwardRef<
    HTMLElement,
    React.HTMLProps<HTMLElement> & PopoverReferProps
>(function PopoverRefer({ children, asChild = false, ...props }, propRef) {
    const context = usePopoverContext();
    const childrenRef = (children as any).ref;
    const ref = useMergeRefs([context.refs.setReference, propRef, childrenRef]);

    // 使用 React.isValidElement 来检查 children 是否是一个 React 元素
    const safeChildren = React.Children.toArray(children).find(child => React.isValidElement(child)) as ReactElement | undefined;
    // 确保 safeChildren 存在并且有 props 属性
    const elementProps = (safeChildren ? safeChildren.props : {}) as any;
    //...children.props   TS2698: Spread types may only be created from object types.
    //自定义版：  `asChild` allows the user to pass any element as the anchor
    if (asChild && React.isValidElement(children)) {
        return React.cloneElement(
            children,
            context.getReferenceProps({
                ref,
                ...props,
                ...elementProps,
                "data-state": context.open ? "open" : "closed"
            })
        );
    }
//正常的按钮版；
    return (
        <button
            ref={ref}
            type="button"
            // The user can style the trigger based on the state
            data-state={context.open ? "open" : "closed"}
            {...context.getReferenceProps(props)}
        >
            {children}
        </button>
    );
});

export const PopoverContent = React.forwardRef<
    HTMLDivElement,
    React.HTMLProps<HTMLDivElement>
>(function PopoverContent({ style, ...props }, propRef) {
    const { context: floatingContext, ...context } = usePopoverContext();
    const ref = useMergeRefs([context.refs.setFloating, propRef]);
    const theme = useTheme();
    if (!floatingContext.open) return null;
    //TS2353: Object literal may only specify known properties, and '"@media print"' does not exist in type 'Properties<string | number, string & {}>'.
    return (
        <FloatingPortal>
            <FloatingFocusManager context={floatingContext} modal={context.modal}>
                <div className="PopoverContent"
                     ref={ref}
                     style={ {
                         zIndex: theme.zIndices.popover,
                         // position: state.strategy,
                         // top: state.y ?? 0,
                         // left: state.x ?? 0,
                         backgroundColor: 'white',
                         boxShadow: theme.shadows.sm,
                         border: '1px solid #ddd',
                         padding: '4px 8px',
                         borderRadius: '4px',
                         "@media print": {
                             boxShadow: 'unset',
                         },
                         ...context.floatingStyles, ...style
                     }as any }
                     aria-labelledby={context.labelId}
                     aria-describedby={context.descriptionId}
                     {...context.getFloatingProps(props)}
                >
                    {props.children}
                </div>
            </FloatingFocusManager>
        </FloatingPortal>
    );
});

export const PopoverHeading = React.forwardRef<
    HTMLHeadingElement,
    React.HTMLProps<HTMLHeadingElement>
>(function PopoverHeading(props, ref) {
    const { setLabelId } = usePopoverContext();
    const id = useId();

    // Only sets `aria-labelledby` on the Popover root element
    // if this component is mounted inside it.
    React.useLayoutEffect(() => {
        setLabelId(id);
        return () => setLabelId(undefined);
    }, [id, setLabelId]);

    return (
        <h4 {...props} ref={ref} id={id}>
            {props.children}
        </h4>
    );
});

export const PopoverDescription = React.forwardRef<
    HTMLParagraphElement,
    React.HTMLProps<HTMLParagraphElement>
>(function PopoverDescription(props, ref) {
    const { setDescriptionId } = usePopoverContext();
    const id = useId();

    // Only sets `aria-describedby` on the Popover root element
    // if this component is mounted inside it.
    React.useLayoutEffect(() => {
        setDescriptionId(id);
        return () => setDescriptionId(undefined);
    }, [id, setDescriptionId]);

    return (
        <div className="PopoverDescription"
             {...props}  ref={ref} id={id}
             style={{
                 display: "flex",
                 flexWrap: 'wrap',
             }}
        />
    );
    // return <p {...props} ref={ref} id={id} />;
});

export const PopoverClose = React.forwardRef<
    HTMLButtonElement,
    React.ButtonHTMLAttributes<HTMLButtonElement>
>(function PopoverClose(props, ref) {
    const { setOpen } = usePopoverContext();
    return (
        <button type="button"
                style={{
                    display: "block",
                    margin: 'auto',
                }}
                ref={ref}
                {...props}
                onClick={(event) => {
                    props.onClick?.(event);
                    setOpen(false);
                }}
        />
    );
});

/* https://floating-ui.com/docs/react
文档例子  https://floating-ui.com/docs/react-examples
跟随弹出列表多列的菜单； https://codesandbox.io/s/nameless-glade-kz3pfc?file=/src/App.tsx
列表Select< >   https://codesandbox.io/s/shy-snowflake-kp6479?file=/src/Select.tsx
注入控制模式
  const [open, setOpen] = useState(false);
  return (
      <Popover open={open} onOpenChange={setOpen}>
        <PopoverRefer onClick={() => setOpen((v) => !v)}>
          启动</PopoverRefer>
        <PopoverContent className="Popover">
          <PopoverHeading>My popover heading</PopoverHeading>
          <PopoverDescription>My popover description</PopoverDescription>
          <PopoverClose>Close</PopoverClose>
        </PopoverContent>
      </Popover>
  );
不受到控制方式
    <Popover>
        <PopoverRefer>启动</PopoverRefer>
        <PopoverContent className="Popover">
            <PopoverHeading>My popover heading</PopoverHeading>
            <PopoverDescription>        </PopoverDescription>
            <PopoverClose>Close</PopoverClose>
        </PopoverContent>
    </Popover>
* */
